// using Node-API promise
#include <iostream>
#include <node_api.h>

using namespace std;

namespace demo {

struct MyPromise {
    bool jsParam;
    napi_async_work async_work;
    napi_deferred deferred;
};

void Execute(napi_env env, void *data)
{
    auto myPromise = reinterpret_cast<MyPromise *>(data);
    cout << "Execute recv " << (myPromise->jsParam ? "true" : "false") << endl;
}

void Complete(napi_env env, napi_status nousb, void *data)
{
    auto myPromise = reinterpret_cast<MyPromise *>(data);
    napi_value result;

    napi_status status = napi_get_boolean(env, myPromise->jsParam, &result);
    if (status != napi_ok) {
        cout << "complete get bool failed\n";
        return;
    }
    // Resolve or reject the promise associated with the deferred depending on
    // whether the asynchronous action succeeded.
    if (myPromise->jsParam) {
        napi_resolve_deferred(env, myPromise->deferred, result);
    } else {
        napi_reject_deferred(env, myPromise->deferred, result);
    }

    // At this point the deferred has been freed, so we should assign NULL to it.
    myPromise->deferred = nullptr;
    napi_delete_async_work(env, myPromise->async_work);
    delete myPromise;
}

napi_value HiPromise(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {0};
    // Get js parameters.
    napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok) {
        cout << "get call back info failed\n";
        return nullptr;
    }

    auto myPromise = new MyPromise();
    status = napi_get_value_bool(env, args[0], &myPromise->jsParam);
    if (status != napi_ok) {
        cout << "get value bool failed\n";
        return nullptr;
    }

    napi_value promise;

    // Create the promise.
    status = napi_create_promise(env, &myPromise->deferred, &promise);
    if (status != napi_ok) {
        cout << "create promise failed\n";
        return NULL;
    }

    // Create resource name.
    napi_value resource = nullptr;
    status = napi_create_string_utf8(env, "hiPromise", NAPI_AUTO_LENGTH, &resource);
    if (status != napi_ok) {
        cout << "create resource name failed\n";
        return nullptr;
    }

    // Create async work.
    status = napi_create_async_work(
        env, nullptr, resource, Execute, Complete, reinterpret_cast<void *>(myPromise), &myPromise->async_work);
    if (status != napi_ok) {
        cout << "create async work failed\n";
        return nullptr;
    }

    status = napi_queue_async_work(env, myPromise->async_work);
    if (status != napi_ok) {
        cout << "queue async work failed\n";
        return nullptr;
    }

    // Return the promise to JS
    return promise;
}

napi_value init(napi_env env, napi_value exports)
{
    napi_status status;
    napi_value fn;

    status = napi_create_function(env, nullptr, 0, HiPromise, nullptr, &fn);
    if (status != napi_ok)
        return nullptr;

    status = napi_set_named_property(env, exports, "hiPromise", fn);
    if (status != napi_ok)
        return nullptr;
    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
} // namespace demo